#include "contentpipe.h"
#include "chanmanager.h"
#include "chanlist.h"
#include "watermarking/watermarkgeneratorfactory.h"
#include "coding/codingmanager.h"
#include <QtConcurrentRun>

namespace Wishmaster
{
	ContentPipe::ContentPipe(ChanManager* manager) : m_manager(manager)
	{
		m_extractor.initialize();
	}

	void ContentPipe::contentFavicon(const QString& chanName, const QByteArray& data)
	{
		Chan* chan = m_manager->chanlist()->getChan(chanName);
		if(!chan)
		{
			log(llError, "ContentPipe: no chan: %s", qPrintable(chanName));
			return;
		}
		QImage img = QImage::fromData(data);
		if(!img.isNull())
		{
			chan->setIcon(img);
			emit chanIconChanged(chanName);
		}
	}

	void ContentPipe::contentBoardIndex(const QString& chanName,
										const QString& boardId,
										int page,
										const QByteArray& data)
	{
		log(llDebug, "ContentPipe::contentBoardIndex");
		Board* board = m_manager->chanlist()->getBoard(chanName, boardId);
		if(!board)
		{
			log(llError, "ContentPipe: no board: %s:%s", qPrintable(chanName), qPrintable(boardId));
			return;
		}

		BoardParser* parser = board->parser();
		if(!parser)
		{
			log(llError, "ContentPipe: no parser for board: %s:%s", qPrintable(chanName), qPrintable(boardId));
			return;
		}

		QList<ThreadInfo> threads = parser->getThreadList(chanName, boardId, data);
		log(llDebug, "ContentPipe::contentBoardIndex, parsed %d threads", threads.count());

		processThreadList(board, threads, page);
		m_manager->updateContent(chanName, boardId);
		board->setRefreshTime(QDateTime::currentDateTime());
	}

	void ContentPipe::processThreadList(Board* board, const QList<ThreadInfo>& threads, int page)
	{
		board->shiftThreads();
		foreach(const ThreadInfo& info, threads)
		{
			ThreadPtr thread = board->getThread(info.thread_id);
			if(!thread)
			{
				thread = ThreadPtr(new Thread(info.thread_id, board));
				thread->setFlag(Thread::Swapped, false);
				thread->setFlag(Thread::Alive, true);
				thread->setName(info.topic);
				QUrl leaderPicUrl = info.leaderPicUrl;
				if(leaderPicUrl.isRelative())
					leaderPicUrl.setHost(board->getParent()->getHost());
				thread->setLeaderPicUrl(leaderPicUrl);

				if(!m_manager->globalFilterList()->checkText(info.topic))
				{
					board->visibleThreads()->push_back(thread);
				}
				else
				{
					board->hiddenThreads()->push_back(thread);
				}
			}
			else
			{
				if((unsigned)thread->getPostsCount() < info.posts && thread->state() != Thread::sNewThread)
				{
					thread->setState(Thread::sNewPosts);
				}
			}
			thread->setThreadTimestamp(QDateTime::fromTime_t(info.timestamp));
			thread->setPostsCount(info.posts);
			if(thread->leaderPic().isNull() && !info.leaderPicUrl.path().isEmpty())
			{
				m_manager->requestThumbnail(PostId(info.thread_id, 0),
											Attachment::NoAttachment, thread->leaderPicUrl());
			}
		}

		board->visibleThreads()->sort();

		m_manager->updateContent(board->getParent()->name(), board->name());
		emit boardUpdated(board->getParent()->name(), board->name());
	}

	void ContentPipe::contentThread(const ThreadId& threadId, const QByteArray& data)
	{
		ThreadPtr thread = m_manager->chanlist()->getThread(threadId);
		if(!thread)
			return;

		BoardParser* parser = parserForBoard(threadId.chanName(), threadId.boardId());
		if(!parser)
			return;

		QList<PostInfo> posts = parser->getPosts(threadId, data);
		processPostList(thread, posts);
		log(llDebug, "ContentPipe::contentThread, parsed %d posts", posts.count());
		filterThread(thread, FilterListPtr());
		extractWatermarks(thread);
		thread->setState(Thread::sUpToDate);
		thread->setTimestamp(QDateTime::currentDateTime());
		m_manager->updateContent(PostId(thread->id(), 0), Attachment::NoAttachment);
	}

	void ContentPipe::processPostList(ThreadPtr& thread, const QList<PostInfo>& posts)
	{
		int postcount = thread->getPostsCount();
		foreach(const PostInfo& info, posts)
		{
			PostId newPostId = info.post_id;
			if(thread->isPostHidden(newPostId))
				continue;

			PostPtr post = thread->lookupPost(newPostId);
			if(!post)
			{
				post = PostPtr(new Post(newPostId));
				//log(llDebug, "post: %s", qPrintable(newPostId.toString()));
				post->setTopic(info.topic);
				post->setName(info.name);
				post->setText(info.text);
				foreach(const ExtraInfo& extra, info.extras)
				{
					post->setExtra(extra.id, extra);
				}

				post->setTimestamp(QDateTime::fromTime_t(info.timestamp));
				addAttachments(info, post);
				thread->appendPost(post);
			}

			for(int i = 0; i < post->attachmentCount(); i++)
			{
				if(!post->attachment(i)->hasThumbnail())
					requestThumbs(post);
				if(thread->flag(Thread::Watched) && !post->attachment(i)->hasData())
					requestAttachments(post);
			}

			foreach(const PostId& reply, info.replies)
			{
				post->addReply(reply);
			}
		}
		//QtConcurrent::run(this, &ContentPipe::filterThread, thread);
		if((thread->getPostsCount() > postcount) && (thread->flag(Thread::Watched)))
			m_manager->newMessages(PostId(thread->id(), 0));
	}

	void ContentPipe::addAttachments(const PostInfo& info, PostPtr& post)
	{
		foreach(const AttachmentInfo& attach, info.attachments)
		{
			QUrl thumbUrl = attach.thumbUrl;
			if(thumbUrl.host().isEmpty())
				thumbUrl.setHost(post->id().chan());
			if(thumbUrl.scheme().isEmpty())
				thumbUrl.setScheme("http");

			QUrl url = attach.url;
			if(url.host().isEmpty())
				url.setHost(post->id().chan());
			if(url.scheme().isEmpty())
				url.setScheme("http");
			post->addAttachment(Attachment::create(thumbUrl, url));
		}
	}

	void ContentPipe::contentThumbnail(const PostId& postId, int attachmentIndex, const QByteArray& data)
	{
		if(postId.postId() != 0)
		{
			PostPtr post = m_manager->chanlist()->getPost(postId);
			if(!post)
				return;

			QImage thumbnail = QImage::fromData(data);
			post->attachment(attachmentIndex)->setThumbnail(thumbnail);
		}
		else // leader pic
		{
			ThreadPtr thread = m_manager->chanlist()->getThread(postId.getThreadId());
			if(!thread)
				return;

			QImage thumbnail = QImage::fromData(data);
			thread->setLeaderPic(thumbnail);
		}
		m_manager->updateContent(postId, attachmentIndex);
	}

	void ContentPipe::contentAttachment(const PostId& postId, int attachmentIndex, const QByteArray& data, const QString& contentType)
	{
		PostPtr post = m_manager->chanlist()->getPost(postId);
		if(!post)
			return;

		post->attachment(attachmentIndex)->setData(data);
		post->attachment(attachmentIndex)->setType(contentType);

		QList<ExtraInfo> extras = m_extractor.extractAll(data);
		log(llDebug, "Extra info: %d", extras.size());
		foreach(ExtraInfo info, extras)
		{
			post->setExtra(info.id, info);
		}
		m_manager->updateAttachment(postId, attachmentIndex);
	}

	BoardParser* ContentPipe::parserForBoard(const QString& chanName, const QString& boardId)
	{
		ChanList* chanlist = m_manager->chanlist();
		Board* board = chanlist->getBoard(chanName, boardId);
		if(!board)
		{
			log(llError, "ContentPipe: unable to find board: %s:%s", qPrintable(chanName), qPrintable(boardId));
			return 0;
		}
		return board->parser();
	}

	void ContentPipe::filterThread(ThreadPtr& thread, const FilterListPtr& filterList)
	{
		if(!filterList)
			return;
		for(int postNumber = 0; postNumber < thread->getActualPostsCount(); postNumber++)
		{
			PostPtr post = thread->getPost(postNumber);
			if(filterList->check(post) && (post->visibility() == Normal))
				post->setVisibility(Hide);
			m_manager->updateContent(post->id(), 0);
		}
	}

	void ContentPipe::extractWatermarks(ThreadPtr& thread)
	{
		for(int postNumber = 0; postNumber < thread->getActualPostsCount(); postNumber++)
		{
			QByteArray result;
			PostPtr post = thread->getPost(postNumber);
			if(WatermarkGeneratorFactory::instance()->probeTextWatermark(post->text(), result))
				CodingManager::instance()->restore(post, result);
		}
	}

	void ContentPipe::requestThumbs(const PostPtr& post)
	{
		for(int i = 0; i < post->attachmentCount(); i++)
		{
			AttachmentPtr attach = post->attachment(i);
			log(llDebug, "thumb: %s", qPrintable(attach->thumbnailUrl().toString()));
			if(attach->thumbnailUrl().scheme() == "http")
				m_manager->requestThumbnail(post->id(), i, attach->thumbnailUrl().toString());
		}
	}

	void ContentPipe::requestAttachments(const PostPtr& post)
	{
		for(int i = 0; i < post->attachmentCount(); i++)
		{
			AttachmentPtr attach = post->attachment(i);
			log(llDebug, "attach: %s", qPrintable(attach->url().toString()));
			if(attach->url().scheme() == "http")
				m_manager->requestPic(post->id(), i);
		}
	}

	void ContentPipe::filterThread(ThreadPtr& thread)
	{
		FilterListPtr filters = m_manager->globalFilterList();
		for(int i = 0; i < thread->getPostsCount(); i++)
		{
			PostPtr post = thread->getPost(i);
			if((post->visibility() == Unhide) || (post->visibility() == Hide))
				continue;
			if(filters->check(post) && (post->visibility() != Unhide))
			{
				post->setVisibility(Hide);
			}
		}
	}
}
